<?php

declare(strict_types=1);
/**
 * This file is part of habo_new_media.
 *
 * @link     https://www.berlen.cn/
 * @contact  tech@berlen.cn
 * @license  http://gitlab.rehou.cn/habo/habo_new_media/-/blob/master/README.md
 */
namespace app\service\cdc\cost;

use app\enum\CostServiceCostEnum;
use app\enum\ServiceCostEnum;
use app\model\ClientAccountModel;
use app\model\CostTaskAccountCompareModel;
use app\model\CostTaskAccountModel;
use app\service\BaseService;
use app\service\client\AccountService;
use app\service\stat\cdc\StatMediaService;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Cache;
use think\facade\Db;

class CostService extends BaseService
{
    /**
     * @var int 操作类型
     */
    protected int $taskAccountType = 0;

    /**
     * @var int|mixed 拉数据服务
     */
    protected ?PullService $pullService;

    /**
     * 设置拉取账户数据.
     * @param ClientAccountModel $model account模型
     * @param int $date 日期
     * @return bool|CostTaskAccountModel
     */
    public function setTaskAccountData(ClientAccountModel $model, int $date = 0)
    {
        if (! FilterService::accountFilter($model, $date)) {
            TaskNoticeService::systemErrorNotice(ServiceCostEnum::ERROR_SYSTEM_CODE, $model->getKey(), '账户不在有效期');
            return self::setError('账户不在有效期');
        }
        // 入库
        $data = [
            'client_id' => $model->getAttr('client_id'),
            'account_id' => $model->getAttr('id'),
            'advertiser_id' => $model->getAttr('advertiser_id'),
            'media_id' => $model->getAttr('media_id'),
            'stat_date' => $date,
            'type' => $this->taskAccountType,
            'status' => ServiceCostEnum::TASK_STATUS_WAIT,
        ];

        return TaskAccountService::insertTaskAccountData($data);
    }

    /**
     * 数据校准.
     * @param CostTaskAccountModel $model 模型
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function setAccountCompare(CostTaskAccountModel $model): bool
    {
        if (! $model->getKey()) {
            return self::setError('cost_task_account表数据不存在');
        }
        // 判断是否校准表的任务
        $isExist = TaskAccountCompareService::getCompareByNewTaskId($model->getKey());
        if ($isExist) {
            return true;
        }
        return TaskAccountCompareService::insertCompareData($model);
    }

    /**
     * 拉取媒体数据.
     * @param CostTaskAccountModel $model 模型
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getDailyData(CostTaskAccountModel $model): bool
    {
        if (! $model->getKey()) {
            return self::setError('cost_task_account数据不存在');
        }

        $redis = Cache::store('redis')->handler();
        // 验证锁
        $lockKey = TaskAccountService::getLockRedisKey($model);
        if ($redis->get($lockKey)) {
            return self::setError('redis锁未释放');
        }
        $redis->setex($lockKey, CostServiceCostEnum::TASK_REDIS_LOCK_TTL, 1); // test

        // 已拉取成功不再拉取
        if ($model->getAttr('status') == CostServiceCostEnum::TASK_STATUS_SUCCESS) {
            return true;
        }
        if (! $this->setPullService($model->getAttr('media_id'))) {
            return self::setError('cost_task_account数据媒体不存在');
        }
        // 设置执行次数+1
        TaskAccountService::setExecuteNum($model);
        return $this->getMediaData($model);
    }

    /**
     * 同步校准时间，删除两表数据（无差异）.
     * @param CostTaskAccountCompareModel $model 校对模型
     */
    public function synAccountCompare(CostTaskAccountCompareModel $model): bool
    {
        Db::startTrans();
        try {
            // 删除数据
            if (! TaskAccountService::deleteTaskAccountData($model->getAttr('task_account_id'))) {
                throw new \Exception(self::getError(0));
            }
            if ($model->getAttr('new_task_account_id')) {
                if (! TaskAccountService::deleteTaskAccountData($model->getAttr('new_task_account_id'))) {
                    throw new \Exception(self::getError(0));
                }
            }
            if (! TaskAccountCompareService::deleteCompareData($model->getKey())) {
                throw new \Exception(self::getError(0));
            }
            // 同步最新时间
            if ($this->taskAccountType == ServiceCostEnum::TASK_TYPE_ACCOUNT) {
                $accountModel = AccountService::getAccountModel($model->getAttr('account_id'));
                if ($accountModel->getAttr('cost_date') < $model->getAttr('stat_date')) {
                    if (! AccountService::synAccountCostTime($accountModel, $model->getAttr('stat_date'))) {
                        throw new \Exception(self::getError(0));
                    }
                }
            }
            Db::commit();
            return true;
        } catch (\Exception $e) {
            Db::rollback();
            return self::setError($e->getMessage());
        }
    }

    /**
     * 更新校准数据(有差异).
     * @param CostTaskAccountCompareModel $model 校对模型
     */
    public function saveAccountCompare(CostTaskAccountCompareModel $model): bool
    {
        Db::startTrans();
        try {
            // 删除数据
            if (! TaskAccountService::deleteTaskAccountData($model->getAttr('task_account_id'))) {
                throw new \Exception(self::getError(0));
            }
            if ($model->getAttr('new_task_account_id')) {
                if (! TaskAccountService::deleteTaskAccountData($model->getAttr('new_task_account_id'))) {
                    throw new \Exception(self::getError(0));
                }
            }
            if (! TaskAccountCompareService::deleteCompareData($model->getKey())) {
                throw new \Exception(self::getError(0));
            }
            // 同步最新时间
            $accountModel = AccountService::getAccountModel($model->getAttr('account_id'));
            if ($accountModel->getAttr('cost_date') < $model->getAttr('stat_date')) {
                if (! AccountService::synAccountCostTime($accountModel, $model->getAttr('stat_date'))) {
                    throw new \Exception(self::getError(0));
                }
            }
            Db::commit();
            return true;
        } catch (\Exception $e) {
            Db::rollback();
            return self::setError($e->getMessage());
        }
    }

    /**
     * 拉取成功操作.
     * @param CostTaskAccountModel $model task模型
     */
    public function pullSuccess(CostTaskAccountModel $model): bool
    {
        if (! $model->getKey()) {
            return self::setError('cost_task_account数据不存在');
        }
        $advertiserId = $model->getAttr('advertiser_id');
        $statDate = intval($model->getAttr('stat_date'));
        $today = intval(date('Ymd'));
        // Db::startTrans();
        try {
            $data = $this->pullService->getResponseData();
            if ($data) {
                if ($statDate == $today) {
                    $this->setReport($model, $data);
                } else {
                    $this->setStat($model, $data);
                }
            }

            // 存主表data数据
            $resultData = [
                'status' => ServiceCostEnum::TASK_STATUS_SUCCESS,
            ];

            if (! TaskAccountService::updateTaskAccountData($model, $resultData)) {
                throw new \Exception(self::getError(0));
            }
            // 存对比表数据
            if (! $this->setAccountCompare($model)) {
                throw new \Exception(self::getError(0));
            }

            // Db::commit();
            return true;
        } catch (\Exception $e) {
            // Db::rollback();
            return self::setError($e->getMessage());
        }
    }

    protected function setReport(CostTaskAccountModel $model, array $data = []): bool
    {
        $advertiserId = $model->getAttr('advertiser_id');
        $statDate = intval($model->getAttr('stat_date'));
        $statService = StatMediaService::getInstance($this->pullService->getMediaId(), $this->taskAccountType);

        foreach ($data as $val) {
            $statModel = $statService->getStatInfo($advertiserId, $statDate, $val);
            if ($statModel) {
                $res = $statService->updateReportData($statModel, $val);
                if (! $res) {
                    throw new \Exception(self::getError(0));
                }
            } else {
                $res = $statService->insertReportData($advertiserId, $val);
                if (! $res) {
                    throw new \Exception(self::getError(0));
                }
            }
        }

        return true;
    }

    protected function setStat(CostTaskAccountModel $model, array $data = []): bool
    {
        $advertiserId = $model->getAttr('advertiser_id');
        $statDate = intval($model->getAttr('stat_date'));

        $statService = StatMediaService::getInstance($this->pullService->getMediaId(), $this->taskAccountType);
        foreach ($data as $val) {
            $statModel = $statService->getStatInfo($advertiserId, $statDate, $val);
            if ($statModel) {
                // 判断是否需要更新
                $isSame = $statService->isSame($statModel, $val);
                if (! $isSame) {
                    $res = $statService->updateStatData($statModel, $val);
                    if (! $res) {
                        throw new \Exception(self::getError(0));
                    }
                }
            } else {
                $res = $statService->insertStatData($advertiserId, $val);
                if (! $res) {
                    throw new \Exception(self::getError(0));
                }
            }
        }

        return true;
    }

    /**
     * 拉取失败操作.
     * @param CostTaskAccountModel $model task模型
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function pullFail(CostTaskAccountModel $model): bool
    {
        if (! TaskAccountService::updateTaskAccountData($model, ['status' => ServiceCostEnum::TASK_STATUS_FAIL])) {
            return false;
        }
        // 提醒
        if ($this->pullService->getResponseMsg()) {
            return TaskNoticeService::mediaErrorNotice($model, $this->pullService);
        }
        return TaskNoticeService::systemErrorNotice(ServiceCostEnum::ERROR_SYSTEM_CODE, $model->getAttr('account_id'), self::getError(0), $model->getKey());
    }

    /**
     * 拉取比较数据.
     * @param CostTaskAccountCompareModel $model 校对模型
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function pullCompareData(CostTaskAccountCompareModel $model): bool
    {
        if ($model->getAttr('new_task_account_id') == 0) {
            return self::setError('比较任务数据未生成');
        }
        $TaskAccountModel = TaskAccountService::getTaskAccountModel($model->getAttr('task_account_id'));
        $compareTaskAccountModel = TaskAccountService::getTaskAccountModel($model->getAttr('new_task_account_id'));
        if ($TaskAccountModel->getAttr('status') == ServiceCostEnum::TASK_STATUS_SUCCESS && $compareTaskAccountModel->getAttr('status') == ServiceCostEnum::TASK_STATUS_SUCCESS) {
            if (! $this->synAccountCompare($model)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 根据媒体设置拉取服务.
     * @param int $mediaId 媒体id
     */
    protected function setPullService(int $mediaId = 0): bool
    {
        $this->pullService = new PullService($mediaId);
        return $this->pullService->isPullService();
    }

    /**
     * 获取媒体数据.
     * @param CostTaskAccountModel $model 任务模型
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    protected function getMediaData(CostTaskAccountModel $model): bool
    {
        while (true) {
            if (! $this->getCostData($model)) {
                // 拉取失败
                self::pullFail($model);
                return false;
            }
            if (! $this->pullService->isResponseSuccess()) {
                // 拉取失败
                self::pullFail($model);
                return false;
            }
            // 拉取成功
            if ($this->pullService->isResponseEmpty()) {
                break;
            }
        }
        if (! self::pullSuccess($model)) {
            return false;
        }
        return true;
    }
}
